#pragma once

#include "CGALDefinition.h"
#include "CGAL_IO_OBJ.h"
#include "MODELOBJDef.h"
// Adaptor for Polyhedron_3
#include <CGAL/Surface_mesh_simplification/HalfedgeGraph_Polyhedron_3.h>
// Simplification function
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
// Stop-condition policy
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_stop_predicate.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_ratio_stop_predicate.h>
// Non-default cost and placement policies 
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Midpoint_and_length.h>  
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_profile.h>
//#include <boost/graph/graph_traits.hpp>

#ifdef MYLIB_API_EXPORT
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif

//custom placement
template<class ECM_>
class My_placement
{
public:

	typedef ECM_ ECM ;

	typedef CGAL::Surface_mesh_simplification::Edge_profile<ECM> Profile ;

	typedef typename CGAL::halfedge_graph_traits<ECM>::Point Point ;

	typedef boost::optional<Point> result_type ;

public:

	My_placement() {}

	result_type operator()( Profile const& aProfile ) const
	{
		return result_type(aProfile.p0());
	}

};
//IK kernel is used
class MYLIB_API Simplify_func
{
private:
	//using kenerl IK
	typedef IK::FT FT;
	typedef Polyhedron_3::Halfedge_handle Halfedge_handle;
	typedef Polyhedron_3::Vertex_iterator Vertex_iterator;
	typedef CGAL::Vector_3<IK> Vector_3;
	typedef CGAL::Surface_mesh_simplification::Edge_profile<Polyhedron_3> Profile;
public:
	//typedef boost::graph_traits<Polyhedron_3>::edges_size_type edges_size_type;
	//typedef bool(*Max_cost_predicate)(kernal::FT const&, Profile const&, edges_size_type, edges_size_type);
	Simplify_func() {}
	~Simplify_func() {}
protected:
	//friend class std::auto_ptr<Parser_obj<kernal,items>>;
	//it should be initialized outside the definition
	static std::auto_ptr<Simplify_func> Simp_instance;

public:
	static Simplify_func* GetSingleton()
	{
		if (NULL == Simp_instance.get())
		{
			Simp_instance.reset(new Simplify_func);
		}
		return Simp_instance.get();
	}
	// do simplification on ModelOBJ

	//simplification using Turk placement
	int Simplification_Turk(ModelOBJ* model,float ratio);

	//simplification using halfedge placement
	int Simplification_Halfedge(ModelOBJ* model,float ratio);

	//eliminate degenerated facets of a polyhedron
	//return the number of degenerated facets
	long eliminate_degenerations(Polyhedron_3& p);

	//merge co-planer facets
	//ftol is the tolerance of normal difference
	//return value is the times of merge
	//methods: find the vertex with all the incident facets within a given normal tolerance, 
	//then collapses the vertex to one of the neighboring vertex
	int merge_coplaner_facets(ModelOBJ* model,double dtol);

	//merge co-planer facets (for cleaning Nef_polyhedron)
	//ftol is the tolerance of normal difference
	//return value is the times of merge
	//methods: find the vertex with all the incident facets within a given normal tolerance, 
	//then collapses the vertex to one of the neighboring vertex
	int clean_nef_polyhedron(Nef_polyhedron_3& poly);

	//merge co-planer facets (for cleaning Polyhedron)
	//ftol is the tolerance of normal difference
	//return value is the times of merge
	//methods: find the vertex with all the incident facets within a given normal tolerance, 
	//then collapses the vertex to one of the neighboring vertex
	int clean_polyhedron(Polyhedron_3& poly);

private:
	//eliminate edge by using edge collapse(used in eliminate_degenerations)
	//h->next()->opposite()->next()->vertex(), h->next()->vertex(), h->opposite()->vertex() and
	//h->opposite()->vertex(), h->opposite()->next()->vertex(), h->opposite()->prev()->opposite()->next()->vertex() etc.)
	bool deg_edge_collapse(Polyhedron_3& poly, Halfedge_handle h);

	//eliminate edge by using edge collapse(used in merge_facets_around_vertex)
	//before the collapse the normalsare test to prevent flip of normal(or directly calculate normal
	//h->next()->opposite()->next()->vertex(), h->next()->vertex(), h->opposite()->vertex() and
	//h->opposite()->vertex(), h->opposite()->next()->vertex(), h->opposite()->prev()->opposite()->next()->vertex() etc.)
	bool edge_collapse(Polyhedron_3* poly, Halfedge_handle h, const Vector_3& refNormal);

	//merge co-planer facets around a vertex (used in merge_coplaner_facets)
	//ftol is the tolerance of normal difference
	//return value is whether merge occurs
	//methods: find the vertex with all the incident facets within a given normal tolerance, 
	//then collapses the vertex to one of the neighboring vertex, the vertex should not be on the boundary.
	bool merge_facets_around_vertex(Polyhedron_3* poly, Vertex_iterator vit, double dTol);
	//custom simplification
// 	bool my_stop_predicte(FT const& aCurrentCost, 
// 		Profile const&//aEdgeProfile
// 		,edges_size_type //aInitialCount
// 		,edges_size_type //aCurrentCount
// 		)
// 	{
// 		bool quitting = aCurrentCost>0.01;
// 		if (quitting)
// 		{
// 			std::cout<<"Quitting, cause current cost("<<aCurrentCost<<")is greater then target cost(0.01)"<<std::endl;
// 			return quitting;				
// 		}
// 	}
	
};

